匯入小工具 (3) - 設定環境變數,才不會被看光光


Posted by 微薄艇 on 2021-07-19

前言

上一篇文章我們使用 DBAPI wrapper package 與 ORM database engine 成功的與資料庫連接,並且使用 SQL/ORM 新增一筆聯絡人到資料庫中。但是別開心得太早,這邊我們會遇上兩個嚴重的問題,一個問題是我們將與資料庫的連線、寫入資料庫的邏輯以及資料庫模型寫在一起,就像個大泥球一樣遲早會過度耦合,未來需要創建更多資料表、欄位,處理更多資料庫的操作時,增加了額外的維護成本也徒增改壞系統的可能性,所以需要幫它瘦身一下按照功能拆分出來。

第二個問題則是,程式碼中關於資料庫的 Connection Strings/Connection URIs,裡頭都使用明碼的呈現無碼,像是資料庫密碼這種敏感資料被人拿到就不好了!又或者專案本身有版本控制,將服務中機敏資料的密鑰或密碼,也一起上到 Git、雲端,增加資料外洩的風險。將它寫入環境變數裡,可以大大的預防、降低風險。引入 .env 檔案做環境變數存放,這邊先不討論各個環境下(development, staging, production)該如何部署的情況。

分流...治理...分而治之

或許可以從歷史借鏡,過去的列強在治理殖民地時,都會根據種族、文化等分化治理,讓本來就關係不好的兩派(或多派)不會起來造反。扯遠了,db.py 一份檔案也能按照邏輯拆成數份檔案,讓我們來分析一下。首先,來盤點一下它做了幾件事情:

  1. 連結資料庫
  2. 資料表模型
  3. 操作資料庫

那是不是能先朝著這三個方面優化。緊接著將 db.py 在額外拆出 crud.pymodels.py 兩份檔案後,三份檔案就會各施其職、更好維護了,如下:

# db.py before
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base


engine = create_engine('postgresql://postgres:secret@localhost:5432/test', echo=True, future=True)
Session = sessionmaker(bind=engine)
Base = declarative_base()
# crud.py
from db import Session
from models import User


session = Session()

session.add(User(name='ed', email='ormtest@gooogle', company='Gooogle'))
person = session.query(User).filter_by(name='ed').first()

session.commit()
# models.py
from sqlalchemy import Column, Integer, String
from db import Base


class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(50), nullable=False)
    email = Column(String(50), nullable=False)
    company = Column(String(50), nullable=False)

孟母三遷引入環境變數

還記得剛剛我們做了什麼嗎?分而治之嘛!這邊也要將 db.py 裡頭的 database URL 移出來,新建兩個檔案 config.py.env,但開始動手前想先談談 python-dotenv。關於 Python 專案的環境變數設定有很多種方法,可以用內建的 libary Configuration,或是待會隆重介紹的 python-dotenv;.env 的形式也有很多種,像是 .ini.json.toml.yaml 等檔案形式,但我想用更簡單的方式呈現,所以選用 python-dotenv 引入專案裡。

先用 pip 套件管理工具,下載 package

pip install python-dotenv

python-dotenv 的運作方式也很簡單,預設就是引入 key-value 結構的 .env 檔案,將設定寫入環境變數中,透過 os.getenv 取得環境變數中的值。

所以我們的 .env 檔案格式如下,會相似於 Bash file

export POSTGRES_USER="postgres"
export POSTGRES_PASSWORD="secret"
export POSTGRES_SERVER="localhost"
export POSTGRES_PORT="5432"
export POSTGRES_DB="test"

export DATABASE_URL="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_SERVER}:${POSTGRES_PORT}/${POSTGRES_DB}"

python-dotenv 的設定中可以省略不加 export,key 值可以選擇加上或不加單引號、雙引號,我更喜歡簡潔的表示如下:

POSTGRES_USER=postgres
POSTGRES_PASSWORD=secret
POSTGRES_SERVER=localhost
POSTGRES_PORT=5432
POSTGRES_DB=test

DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_SERVER}:${POSTGRES_PORT}/${POSTGRES_DB}

準備好 .env 檔就可以呼叫 load_dotenv() 載入 .env,config.py 如下:

# config.py
import os
from dotenv import load_dotenv


load_dotenv()
SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL')

Database URL 透過環境變數的引入,db.py 只要透過 config.py 取得 Database URL,就能正常與資料庫連線。

# db.py after
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from config import SQLALCHEMY_DATABASE_URI


engine = create_engine(SQLALCHEMY_DATABASE_URI, echo=True, future=True)
Session = sessionmaker(bind=engine)
Base = declarative_base()

是不是很簡單又方便使用,還有很多進階的 python-dotenv 使用方法,建議到文件裡查閱。

結論

回顧一下本次所學到的內容。為了好維護專案程式碼,將功能切分一小塊、拆分成各自的服務,分而治之並讓它們各施其職,做好自己職責。將機敏資料寫在 .env file 裡,透過 config.py 用環境變數的方式引入,db.py 就能從 config 裡拿到 Database URL,也保護專案的裡頭重要的密碼、密鑰,不會這麼容易被取得。

下一章節會再把主軸拉回到匯入小工具身上,會談的是如何讀取 csv 檔案,將資料整理過後寫入資料庫,也要判斷資料是否已經存在資料庫裡,資料表格式不符合預期該怎麼處理,那我們就下一章節見。

參考


#Python #environment variable #.env #python-dotenv #dotenv







Related Posts

MVC V.S. MVVM 學習筆記

MVC V.S. MVVM 學習筆記

【THM Walkthrough】Lateral Movement and Pivoting (2)

【THM Walkthrough】Lateral Movement and Pivoting (2)

修改 Sublime Tab 預設為空兩格

修改 Sublime Tab 預設為空兩格


Comments